import web
import logging
from google.appengine.api import mail
from email.utils import parseaddr
from email.header import decode_header
from urllib2 import HTTPError
import mimetypes
import simplejson
import gcp
import printer
import config
import ho.pisa as pisa


urls = (
        '/_ah/mail/(.+)', 'MailHandler',
        )

app = web.application(urls, globals())


class UnrecognizedReceiver(Exception):
    def __init__(self, receiver):
        self.receiver = receiver
    def __str__(self):
        return repr('Unrecognized receiver: %s' % self.receiver)

class AccessDenied(Exception):
    def __init__(self, sender, printer):
        self.sender = sender
        self.printer = printer
    def __str__(self):
        return repr('Sender: %s can not access printer: %s' % (self.sender, self.printer.printerid))

class NoPrintableContent(Exception):
    def __init__(self):
        pass
    def __str__(self):
        return repr('No printable content')

class MailHandler:
    @classmethod
    def SendMail(cls, to, subject, body, sender=config.emailsender):
        mail.send_mail(sender=sender,
                       to=to,
                       subject=subject,
                       body=body,
                       )

    @classmethod
    def convertBody(cls, body, html, attachments):
        if html:
            pdf = pisa.CreatePDF(html.encode('UTF-8'))
            content = pdf.dest.getvalue()
            logging.info(content)
            filename = "body.pdf"
        else:
            logging.info(body)
            content = body
            filename = "body.txt"
        
        return (filename, content)
    
    @classmethod
    def HandlePrinterMail(cls, addr, mail_message):
        try:
            printto = printer._Printer.FromPublishEmail(addr)
        
            if not (printto and printto.eprint_enabled): # can not find destination printer or email print is not enabled
                raise UnrecognizedReceiver(addr)
        
            try:
                sender = mail_message.sender
                sender = parseaddr(sender)[-1]
            except:
                pass
        
            if not printto.AcceptSender(sender): # sender is not allowed to print via this email address
                raise AccessDenied(sender, printto)

            # gcp api object
            api = gcp.GoogleCloudPrint(user=printto.user)
            failed_files = [] # failed contents; list of tuples [(file1, 'unsupported'), (file2, 'unsupprted')]
            u_subject = decode_header(mail_message.subject)[0][0] #email subject

            try:
                attachments = mail_message.attachments
            except AttributeError:
                attachments = []

            try:
                body = mail_message.body
                body = body.decode()
            except AttributeError:
                body = None
            
            try:
                html = mail_message.html
                html = html.decode()
            except AttributeError:
                html = None

            # print body...
            body_content = cls.convertBody(body, html, attachments)
            if body_content:
                jobtitle = u_subject
                contenttype = mimetypes.guess_type(body_content[0])[0] or 'application/octet-stream'
                try:
                    
                    rst = api.submit_print_job(printerid=printto.printerid,
                                               contentType=contenttype,
                                               content=body_content,
                                               title=jobtitle,
                                               )
                    logging.info('submit result: %s' % rst)
                    result = simplejson.loads(rst)
                    if not bool(result['success']):
                        failed_files.append(('email body', result['message']))
                except HTTPError:
                    # TODO: permission problem?
                    # Get a new token OR delete account forever
                    pass


            # print attachments...
            for filename, content in attachments:
                u_filename = decode_header(filename)[0][0]
                jobtitle = u_subject + ':' + u_filename
                
                logging.info('ready to submit file (%s) to cloud print' % u_filename)
                contenttype = mimetypes.guess_type(u_filename)[0] or 'application/octet-stream'
                try:
                    rst = api.submit_print_job(printerid=printto.printerid,
                                               contentType=contenttype,
                                               content=(u_filename, content.decode()),
                                               title=jobtitle,
                                               )
                    logging.info('submit result: %s' % rst)
                    result = simplejson.loads(rst)
                    if not bool(result['success']):
                        failed_files.append((u_filename, result['message']))
                except HTTPError:
                    # TODO: permission problem?
                    # Get a new token OR delete account forever
                    pass
                
            # send a mail to user if any submission fails
            if failed_files:
                body = [
                        'The following files are not successfully submitted to Google Cloud Print for some reason.',
                        ]
                for filename, reason in failed_files:
                    body.append('%s: %s' % filename, reason)
                cls.SendMail(mail_message.sender, 'Re: %s' % mail_message.subject, ''.join(body))
            
        except AccessDenied, e:
            logging.error(e.__str__())
            cls.SendMail(mail_message.sender, 'Re: %s' % mail_message.subject, e.__str__())
        except UnrecognizedReceiver, e:
            logging.error(e.__str__())
            cls.SendMail(mail_message.sender, 'Re: %s' % mail_message.subject, e.__str__())
    
    def POST(self, addr):
        mail_message = mail.InboundEmailMessage(web.data())
        self.HandlePrinterMail(addr, mail_message)
        

if __name__ == '__main__':
    app.cgirun()


